package demo.android.myapplication.demos.touch

import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import android.widget.OverScroller
import androidx.core.view.GestureDetectorCompat
import cn.android.basic.*
import demo.android.myapplication.R
import kotlin.math.max
import kotlin.math.min

/**
 * <br>createBy Gw
 * <br>createTime: 2021/7/16 16:19
 */
const val MAX_SCALE = 2.3f
const val MIN_SCALE = 1.0f

class ScalableImageView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private lateinit var source: Bitmap
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    //缩小动画
    private val zoomOut = ObjectAnimator.ofFloat(this, "scale", MAX_SCALE, MIN_SCALE)
    private val scroller:OverScroller = OverScroller(context)
    private val gestureDetector: GestureDetectorCompat = GestureDetectorCompat(context, GestureListener())
    private val scaleGestureDetector = ScaleGestureDetector(context, ScaleGestureListener())

    private var offsetX = 0f
    private var offsetY = 0f
    private var initOffsetY:Float = 0f

    private var scale = MIN_SCALE
        set(value) {
            field = value
            invalidate()
        }

    private val big get() = scale == MAX_SCALE

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        source = decodeBitmap(R.mipmap.pujing_avatar, width)
        initOffsetY = ((height - source.height) / 2).float
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val ratio = (scale - MIN_SCALE) / (MAX_SCALE - MIN_SCALE)
        canvas.translate(offsetX * ratio, offsetY * ratio)
        canvas.scale(scale, scale, centerX, centerY)
        canvas.drawBitmap(source, 0F, initOffsetY, paint)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        val result = scaleGestureDetector.onTouchEvent(event)
        if(!scaleGestureDetector.isInProgress) {
            gestureDetector.onTouchEvent(event)
        }
        return result
    }

    private fun fixBoundary(scale:Float) {
        val boundaryOffsetX = (source.width * scale - width) / 2
        val boundaryOffsetY = (source.height * scale - height) / 2

        offsetX = max(offsetX, -boundaryOffsetX)
        offsetX = min(offsetX, boundaryOffsetX)
        offsetY = max(offsetY, -boundaryOffsetY)
        offsetY = min(offsetY, boundaryOffsetY)
    }

    inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
        override fun onDown(e: MotionEvent?): Boolean {
            return true
        }

        override fun onScroll(
            e1: MotionEvent?,
            e2: MotionEvent?,
            distanceX: Float,
            distanceY: Float
        ): Boolean {

            if (scale != MIN_SCALE) {
                offsetX -= distanceX
                offsetY -= distanceY
                fixBoundary(scale)
            }

            invalidate()
            return true
        }

        override fun onFling(
            e1: MotionEvent?,
            e2: MotionEvent?,
            velocityX: Float,
            velocityY: Float
        ): Boolean {
            if (scale != MIN_SCALE) {
                val boundaryOffsetX = ((source.width * scale - width) / 2).int
                val boundaryOffsetY = ((source.height * scale - height) / 2).int
                scroller.fling(offsetX.int, offsetY.int, velocityX.int, velocityY.int
                    , -boundaryOffsetX, boundaryOffsetX, -boundaryOffsetY, boundaryOffsetY)
                postOnAnimation (object : Runnable{
                    override fun run() {
                        if(scroller.computeScrollOffset()){
                            offsetX = scroller.currX.float
                            offsetY = scroller.currY.float
                            invalidate()
                            postOnAnimation(this)
                        }
                    }
                })
            }
            return true
        }

        override fun onDoubleTap(e: MotionEvent): Boolean {
            if (!big) {
                offsetX = (e.x - centerX) * (1 - MAX_SCALE / MIN_SCALE)
                offsetY = (e.y - centerY) * (1 - MAX_SCALE / MIN_SCALE)

                fixBoundary(MAX_SCALE)
                zoomOut.reverse()
            } else {
                zoomOut.start()
            }
            return true
        }
    }

    inner class ScaleGestureListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {

        override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
            return true
        }

        override fun onScale(detector: ScaleGestureDetector): Boolean {
            offsetX = (detector.focusX - centerX) * (1 - MAX_SCALE / MIN_SCALE)
            offsetY = (detector.focusY - centerY) * (1 - MAX_SCALE / MIN_SCALE)
            val localScale = scale * detector.scaleFactor
            if(localScale < MIN_SCALE) {
                return false
            }
            if(localScale > MAX_SCALE){
                return false
            }
            scale = localScale
            return true
        }

        override fun onScaleEnd(detector: ScaleGestureDetector) {
            super.onScaleEnd(detector)
        }
    }

}